--[[
Get->
_ : group for items
_c : function to replace the default Get

_chd: get sub nodes
'
`m for match
`w for where sort limit
`f for fix sub node, and support match
'
--]]
local dfskey=zdc.fieldutil.dfskey
local api=require'zdc.nodeapi'
local autostr2table=require'zdc.sandbox'.autostr2table
local buildrefid=require'zdc.typeex'.DeltaidToRefid
module(..., package.seeall)

actions={}

local function getrefid(user,dal,n,k,refid,keys,deep)
	if not refid or #refid==0 then return end
	local rkv=autostr2table(keys)
	if type(rkv)~='table' or rkv['*'] then rkv=autostr2table(n[k .. 'Keys'] or n[k:sub(1,3) .. 'Keys']) or {} end
	deep = rkv._deep or n[k .. 'Deeps'] or n[k:sub(1,3) .. 'Deeps'] or deep
	local rn=dal:GetNode(refid)
	local tv
	if rn then
		local _call=rkv._c or 'zGet'
		local _cd
		if _call=='zGet' then _cd=deep end
		tv=rn(_call,rkv,_cd)
	end
	return tv
end

local function getrefidlst(user,dal,n,k,refidlst,keys,deep)
	local lst=zdc.typeex.RefidlstToTable(refidlst)
	local tv={}
	for i=1,#lst do
		local r=getrefid(user,dal,n,k,lst[i],keys,deep)
		if r then table.insert(tv,r) end
	end
	return tv
end
local function getdeltarefidlst(user,dal,n,k,refidlst,fd,keys,deep)
	local base=fd.delta
	local rootid=((base and n[base]) or fd.path or n[k .. 'Base'] or n[k:sub(1,3) .. 'Base'])

	local lst=zdc.typeex.RefidlstToTable(refidlst)
	local tv={}
	for i=1,#lst do
		local r=getrefid(user,dal,n,k,rootid..'/'..lst[i],keys,deep)
		if r then table.insert(tv,r) end
	end
	return tv
end

local function builddefaultargs(n,args)
	if type(args)=='table' and next(args) then return args end
	args={['`m.']={['*']=true}}
	return args
end
function actions.zGet(user,dal,n,act,args,deep)
	args=builddefaultargs(n,args)
	local tv={}
	if type(args)=='table' then
		for k,v in pairs(args) do
			local tp=type(v)
			if type(k)=='string' and string.byte(k,1)==95 then
				if k=='_m' then
					-- get node metadata
					--TODO: secu for meta
					local _m=tv._m or {}
					if tp=='table' then
						for k1,v1 in pairs(v) do
							if type(k1)=='number' then
								local __v='__'..v1
								_m[v1]=rawget(n,__v)
							else
								local __v='__'..k1
								_m[k1]=rawget(n,__v)
							end
						end
					elseif tp=='string' then
						local __v='__'..v
						_m[v]=rawget(n,__v)
					else
						_m.id=n.__id
						_m.ik=n.__ik
						_m.ts=n.__ts
					end
					tv._m=_m
				elseif k=='_mi' then
					local _mi=tv._mi or {}
					local opt,verb,tym,cdm
					if tp=='table' then
						opt,verb,tym,cdm=v.opt,v.verb,v.tym,v.cdm
					elseif tp=='number' then
						opt,verb=nil,tp
					else
					end
					zdc.nodeapi.metainfo(user,dal,n,_mi,opt,verb,tym,cdm)
					tv._mi=_mi
				elseif k=='_a' then
					-- do a call in get
					-- _a={act='xxx',args={...}}
					tv._a={select(2,assert(zdc.nodeapi.callact(user,dal,n,v.act,args)))}
				elseif k=='_cnt' then
					local wh
					local tvv=type(v)
					if tvv=='string' then
						wh=v
					elseif tvv=='table' then
						wh=v.w
					end
					tv._cnt=dal:GetChildCntEx(n.__id,wh)
				end
			elseif type(k)=='string' and k:sub(1,2)=='`m' then
				local mp=k:sub(3)
				local ms=n:__get_fields(nil,n._fieldGroup,nil,mp)
				local vgrp
				if tp=='table' then
					vgrp=(v._ and (tv[v._] or {})) or tv
				elseif tp=='string' or tp=='number' then
					vgrp=tv[v] or {}
				else
					vgrp=tv
				end
				for _k,_v in pairs(ms) do
					if tp=='table' and _v.get then
						local getstyle=_v.get
						if getstyle=='REFID' then
							vgrp[_k]=getrefid(user,dal,n,_k,n[_k],v)
						elseif getstyle=='DELTAID' then
							vgrp[_k]=getrefid(user,dal,n,_k,buildrefid(n,_v,_k),v)
						elseif getstyle=='REFIDLST' then
							vgrp[_k]=getrefidlst(user,dal,n,_k,n[_k],v)
						elseif getstyle=='DELTAIDLST' then
							vgrp[_k]=getdeltarefidlst(user,dal,n,_k,_v,v)
						else
							vgrp[_k]=n[_k]
						end
					elseif tp=='string' or tp=='number' then
						vgrp[v]=n[_k]
					else
						vgrp[_k]=n[_k]
					end
				end
				if tp=='table'then
					if v._ then tv[v._]=vgrp end
				elseif tp=='string' or tp=='number' then
					tv[v]=vgrp
				end
			elseif type(k)=='string' and k:sub(1,2)=='`g' then
				local mp=k:sub(3)
				local l1,l2
				l1=tonumber(mp)
				if not l1 then
					l1,l2=string.match(mp,'(-?%d+%.?%d*),(-?%d+%.?%d*)')
				else
					l2=l1+1
				end
				local ms=n:__get_fields(nil,tonumber(l1),tonumber(l2),mp)
				local vgrp
				if tp=='table' then
					vgrp=(v._ and (tv[v._] or {})) or tv 
				elseif tp=='string' or tp=='number' then
					vgrp=tv[v] or {}
				else
					vgrp=tv
				end
				for _k,_v in pairs(ms) do
					if tp=='table' and _v.get then
						local getstyle=_v.get
						if getstyle=='REFID' then
							vgrp[_k]=getrefid(user,dal,n,_k,n[_k],v)
						elseif getstyle=='DELTAID' then
							vgrp[_k]=getrefid(user,dal,n,_k,buildrefid(n,_v,_k),v)
						elseif getstyle=='REFIDLST' then
							vgrp[_k]=getrefidlst(user,dal,n,_k,n[_k],v)
						elseif getstyle=='DELTAIDLST' then
							vgrp[_k]=getdeltarefidlst(user,dal,n,_k,_v,v)
						else
							vgrp[_k]=n[_k]
						end
					elseif tp=='string' or tp=='number' then
						vgrp[v]=n[_k]
					else
						vgrp[_k]=n[_k]
					end
				end
				if tp=='table'then
					if v._ then tv[v._]=vgrp end
				elseif tp=='string' or tp=='number' then
					tv[v]=vgrp
				end
			else
				local fd=n:__get_field_define(k)
				if fd then
					if tp=='table' and fd.get then
						local getstyle=fd.get
						if getstyle=='REFID' then
							tv[v._ or k]=getrefid(user,dal,n,k,n[k],v)
						elseif getstyle=='DELTAID' then
							tv[v._ or k]=getrefid(user,dal,n,k,buildrefid(n,fd,k),v)
						elseif getstyle=='REFIDLST' then
							tv[v._ or k]=getrefidlst(user,dal,n,k,n[k],v)
						elseif getstyle=='DELTAIDLST' then
							tv[v._ or k]=getdeltarefidlst(user,dal,n,k,fd,v)
						else
							tv[k]=n[k]
						end
						
					else
						if tp=='string' or tp=='number' then
							tv[v]=n[k]
						else
							tv[k]=n[k]
						end
					end
				end
			end
		end

		--deal sub nodes
		local cdeep=args._deep or tonumber(deep) or 0
		local chd=autostr2table(args._chd)
		if chd then
			local rv={}
			local emptytb={}
			local chdtype=type(chd)
			if chdtype=='table' then
				local chdcdeep=chd._deep or (cdeep)
				for k_,v_ in pairs(chd) do
					local k,v=k_,autostr2table(v_)
					local ty2=type(v)
					local CNID=n.__id
					if type(k)=='string' then
						local ksub2=k:sub(1,2)
						if ksub2=='`j' then
							local j
							j,k=string.match(k,"(.-)`(.*)",3)
							CNID=CNID..'/'..j
						end
						if k=='_deep' then
						elseif ksub2=='`m' then
							local mp=k:sub(3)
							if ty2=='table' then
								local allsub=dal:GetChildNodes(CNID)
								local vgrp= (v._ and (tv[v._] or {})) or rv
								local _call=v._c or 'zGet'
								local _cd
								if _call=='zGet' then _cd=chdcdeep-1 end
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if chdcdeep>0 then
											vgrp[subk]=subv(_call,v,_cd)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								if v._ then tv[v._]=vgrp end
							elseif ty2=='number' then
								local allsub=dal:GetChildNodes(CNID)
								for subk,subv in pairs(allsub) do
									if rv[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if v>0 then
											rv[subk]=subv('zGet',nil,v)
										else
											rv[subk]=emptytb
										end
									end
								end
							elseif ty2=='string' then
								local vgrp=tv[v] or {}
								local allsub=dal:GetChildNodes(CNID)
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if chdcdeep>0 then
											vgrp[subk]=subv('zGet',nil,chdcdeep-1)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								tv[v]=vgrp
							else
								--todo: no use on this condition
							end
						elseif ksub2=='`w' then
							local mp=k:sub(3)
							if ty2=='table' then
								if v._ids then
									local alllst=dal:GetChildIdsEx(CNID,assert(string.match(mp,"(.*)`(.*)`(.*)")))
									if v._ then
										tv[v._]=alllst
									else
										rv=alllst
									end
								elseif v._rf then
									local alllst=dal:GetChildRawFieldsEx(CNID,v._rf,assert(string.match(mp,"(.*)`(.*)`(.*)")))
									if v._ then
										tv[v._]=alllst
									else
										rv=alllst
									end
								else
									local vgrp= (v._ and (tv[v._] or {})) or rv
									local _call=v._c or 'zGet'
									local _cd
									if _call=='zGet' then _cd=chdcdeep-1 end
									local allsub,alllst=dal:GetChildNodesEx(CNID,assert(string.match(mp,"(.*)`(.*)`(.*)")))
									for subk,subv in pairs(allsub) do
										if vgrp[subk]==nil then
											if chdcdeep>0 then
												vgrp[subk]=subv(_call,v,_cd)
											else
												vgrp[subk]=emptytb
											end
										end
									end
									if v._ then
										tv[v._]=vgrp
										tv[v._..'_i']=alllst
									end
								end
							elseif ty2=='number' then
								local allsub,alllst=dal:GetChildNodesEx(CNID,assert(string.match(mp,"(.*)`(.*)`(.*)")))
								for subk,subv in pairs(allsub) do
									if rv[subk]==nil then
										if v>0 then
											rv[subk]=subv('zGet',nil,v)
										else
											rv[subk]=emptytb
										end
									end
								end
							elseif ty2=='string' then
								local vgrp=tv[v] or {}
								local allsub,alllst=dal:GetChildNodesEx(CNID,assert(string.match(mp,"(.*)`(.*)`(.*)")))
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil then
										if chdcdeep>0 then
											vgrp[subk]=subv('zGet',nil,chdcdeep-1)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								tv[v]=vgrp
							else
								--todo: no use on this condition
							end
						elseif ksub2=='`f' then
							local mp=k:sub(3)
							if ty2=='table' then
								local __rnt=dal:GetNode(CNID)
								local allsub=__rnt:__get_child_fix()
								local vgrp= (v._ and (tv[v._] or {})) or rv
								local _call=v._c or 'zGet'
								local _cd
								if _call=='zGet' then _cd=chdcdeep-1 end
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if chdcdeep>0 then
											vgrp[subk]=subv(_call,v,_cd)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								if v._ then tv[v._]=vgrp end
							elseif ty2=='number' then
								local __rnt=dal:GetNode(CNID)
								local allsub=__rnt:__get_child_fix()
								for subk,subv in pairs(allsub) do
									if rv[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if v>0 then
											rv[subk]=subv('zGet',nil,v)
										else
											rv[subk]=emptytb
										end
									end
								end
							elseif ty2=='string' then
								local vgrp=tv[v] or {}
								local __rnt=dal:GetNode(CNID)
								local allsub=__rnt:__get_child_fix()
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and (#mp==0 or string.match(subk,mp)) then
										if chdcdeep>0 then
											vgrp[subk]=subv('zGet',nil,chdcdeep-1)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								tv[v]=vgrp
							else
								--todo: no use on this condition
							end
						elseif ksub2=='`p' then -- pto is come from ?
							local mp=k:sub(3)
							if ty2=='table' then
								local allsub=dal:GetChildNodes(CNID)
								local vgrp= (v._ and (tv[v._] or {})) or rv
								local _call=v._c or 'zGet'
								local _cd
								if _call=='zGet' then _cd=chdcdeep-1 end
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and subv:__is_come_from(mp) then
										if chdcdeep>0 then
											vgrp[subk]=subv(_call,v,_cd)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								if v._ then tv[v._]=vgrp end
							elseif ty2=='number' then
								local allsub=dal:GetChildNodes(CNID)
								for subk,subv in pairs(allsub) do
									if rv[subk]==nil and subv:__is_come_from(mp) then
										if v>0 then
											rv[subk]=subv('zGet',nil,v)
										else
											rv[subk]=emptytb
										end
									end
								end
							elseif ty2=='string' then
								local vgrp=tv[v] or {}
								local allsub=dal:GetChildNodes(CNID)
								for subk,subv in pairs(allsub) do
									if vgrp[subk]==nil and subv:__is_come_from(mp) then
										if chdcdeep>0 then
											vgrp[subk]=subv('zGet',nil,chdcdeep-1)
										else
											vgrp[subk]=emptytb
										end
									end
								end
								tv[v]=vgrp
							else
								--todo: no use on this condition
							end
						else
							local subv=dal:GetNode(CNID..'/'..k)
							if subv then
								if ty2=='table' then
									if chdcdeep>0 then
										local _call=v._c or 'zGet'
										local _cd
										if _call=='zGet' then _cd=chdcdeep-1 end
										rv[v._ or k]=subv(_call,v,_cd)
									else
										rv[v._ or k]=emptytb
									end
								elseif ty2=='number' then
									if v>0 then
										rv[k]=subv('zGet',nil,v)
									else
										rv[k]=emptytb
									end
								elseif ty2=='string' then
									if chdcdeep>0 then
										rv[v]=subv('zGet',nil,chdcdeep-1)
									else
										rv[v]=emptytb
									end
								else
									if chdcdeep>0 then
										rv[k]=subv('zGet',nil,chdcdeep-1)
									else
										rv[k]=emptytb
									end
								end
							end
						end
					else
					end
				end
			elseif chdtype=='number' then
				deep=chd
				if deep>0 then
					local allsub=n:__get_child_all()
					for subk,subv in pairs(allsub) do
						rv[subk]=subv("zGet",nil,deep-1)
					end
				else
					local allsub=n:__get_child_all()
					for subk,subv in pairs(allsub) do
						rv[subk]=emptytb
					end
				end
			else
			end
			tv._chd=rv
		else
			if cdeep>0 then
				local allsub=n:__get_child_all()
				local rv={}
				for subk,subv in pairs(allsub) do
					if rv[subk]==nil then
						rv[subk]=subv('zGet',nil,cdeep-1)
					end
				end
				tv._chd=rv
			end
		end
	end
	return tv
end

function actions.zSet(user,dal,n,act,args)
	local r,msg=api.mutilset(user,dal,n,args)
	if r then dal:CommitTA() end
	return r,msg
end

function actions.zUpdate(user,dal,n,act,args)
	local r,msg=api.updatesub(user,dal,n,args.id,args.mc,args.vals)
	if r then dal:CommitTA() end
	return r and true,msg
end

function actions.zCreate(user,dal,n,act,args)
	local r,msg=api.createsub(user,dal,n,args.id,args.mc,args.vals)
	if r then dal:CommitTA() end
	return r and true,msg
end

function actions.zDelete(user,dal,n,act,args)
	local r,msg
	if type(args)=='table' then
		r,msg=api.mdelsub(user,dal,n,args)
	else
		r,msg=api.delsub(user,dal,n,args)
	end
	if r then dal:CommitTA() end
	return r,msg
end

function actions.zClone(user,dal,n,act,args)
	local r,msg=api.clone(user,dal,n,args.toid,args.opt)
	return r,msg
end

function actions.zMetaInfo(user,dal,n,act,args)
	local r,msg=api.metainfo(user,dal,n,nil,args.opt,args.verb,args.tym,args.cdm)
	return r,msg
end